// -----------------------------------------------------------------------
//  <copyright file="ResourceService.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2025 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2025/1/8 18:30</last-date>
// -----------------------------------------------------------------------

using DaprPlus.Authorization.Application.Commands;
using DaprPlus.Authorization.Domain.Entities;
using DaprPlus.Authorization.Domain.IntegrationEvents;
using DaprPlus.Authorization.Domain.Specs;
using DaprPlus.Authorization.Properties;
using DaprPlus.Filters;

using OSharp.Collections;
using OSharp.Extensions;


namespace DaprPlus.Authorization.Domain;

public class ResourceService(IServiceProvider provider) : IResourceService
{
    private readonly IRepository<Resource> _repository = provider.GetRepository<Resource>();
    private readonly ILogger _logger = provider.GetLogger<ResourceService>();

    /// <summary>
    /// 创建资源
    /// </summary>
    /// <param name="resource">提取的资源信息</param>
    /// <param name="token"></param>
    /// <returns></returns>
    public async Task<ApiResult> CreateResourceAsync(IResource resource, CancellationToken token)
    {
        if (resource is not Resource resource2)
        {
            return ApiResult.Error("资源信息不正确");
        }

        resource2.AllowedOperationsJson = GetAllowedResourceOperationsJson(resource2, null, false);
        await _repository.InsertAsync(resource2, token);
        _logger.LogInformation(ResAuthorization.Format_Auth_AddResource, resource.ServiceName, resource.Type,
            resource.Id, resource.Name, resource.Code);
        return ApiResult.Success($"类型为 {resource.Type} 的资源 {resource.Name} 在服务 {resource.ServiceName} 中创建成功");
    }

    /// <summary>
    /// 使用提取的资源更新数据库资源
    /// </summary>
    /// <param name="resource">提取的资源信息</param>
    /// <param name="sync">是否初始化同步，否则为后台更新</param>
    /// <param name="token"></param>
    /// <returns></returns>
    public async Task<ApiResult> UpdateResourceAsync(IResource resource, bool sync, CancellationToken token)
    {
        var spec = new ResourceByCodeAndServiceNameAndTypeSpec(resource.Code, resource.ServiceName, resource.Type);
        var dbResource = await _repository.GetAsync(spec, token);
        if (dbResource == null)
        {
            return ApiResult.NoChanged();
        }

        var hash = dbResource.GetHash();
        var metadata = resource.GetMetadata();
        var metadataJson = metadata.Count > 0 ? metadata.ToTextJson() : null;
        dbResource.AllowedOperationsJson = GetAllowedResourceOperationsJson(resource, dbResource, sync);

        if (sync)
        {
            if (resource.IsSystem)
            {
                dbResource.Name = resource.Name;
                dbResource.Description ??= resource.Description;
                dbResource.MetadataJson = metadataJson;
                if (!dbResource.IsAccessTypeChanged)
                {
                    dbResource.AccessType = resource.AccessType;
                }
            }
        }
        else
        {
            dbResource.Description = resource.Description;
            dbResource.IsLocked = resource.IsLocked;
            dbResource.AccessType = dbResource.Type == ResourceType.Entity ? ResourceAccessType.None : resource.AccessType;

            if (dbResource.AccessType != resource.AccessType)
            {
                dbResource.IsAccessTypeChanged = true;
            }

            if (resource.IsSystem)
            {
                dbResource.IsAccessTypeChanged = dbResource.AccessType != resource.AccessType;
            }
            else
            {
                dbResource.Name = resource.Name;
                dbResource.Code = resource.Code;
                dbResource.ServiceName = resource.ServiceName;
                dbResource.Type = resource.Type;
                dbResource.MetadataJson = metadataJson;

                // 如果是 Resource 类型，处理 AllowedOperationsJson
                if (resource is Resource resource2)
                {
                    dbResource.AllowedOperationsJson = resource2.AllowedOperationsJson;
                }
            }
        }

        if (dbResource.GetHash() == hash)
        {
            return ApiResult.NoChanged();
        }

        // 资源更新集成事件
        var eventDto = new ResourceIntegrationEventDto(dbResource.Code, dbResource.Type);
        var serviceName = provider.GetServiceName();
        dbResource.AddIntegrationEvent(new ResourceUpdatedIntegrationEvent([eventDto], serviceName));

        await _repository.UpdateAsync(dbResource, token);

        _logger.LogInformation($"资源 {resource.Name} 更新成功");
        return ApiResult.Success($"资源 {resource.Name} 更新成功");
    }

    private string? GetAllowedResourceOperationsJson(IResource resource, Resource? dbResource, bool sync)
    {
        var source = resource.GetAllowedOperations().ToList();
        var target = dbResource == null ? source : dbResource.GetAllowedOperations().ToList();

        //新增
        var items = source.Except(target, EqualityHelper<ResourceOperation>.CreateComparer(m => m.Code)).ToArray();
        if (items.Length > 0)
        {
            target = target.Union(items).ToList();
        }

        //删除
        items = target.Except(source, EqualityHelper<ResourceOperation>.CreateComparer(m => m.Code)).ToArray();
        if (items.Length > 0)
        {
            target.RemoveAll(m => items.Contains(m));
        }

        //更新
        foreach (var entry in target)
        {
            // 处理 AccessType
            var sourceEntry = source.FirstOrDefault(m => m.Code == entry.Code);
            if (sourceEntry != null)
            {
                if (sync)
                {
                    // 同步阶段：如果是系统资源且 IsAccessTypeChanged=false，则更新 AccessType
                    if (resource.IsSystem && dbResource is not { IsAccessTypeChanged: true })
                    {
                        entry.AccessType = sourceEntry.AccessType;
                    }
                }
                else
                {
                    // 手动更新阶段：如果 AccessType 不同，更新并标记 IsAccessTypeChanged=true
                    if (entry.AccessType != sourceEntry.AccessType)
                    {
                        entry.AccessType = sourceEntry.AccessType;
                        if (resource.IsSystem)
                        {
                            entry.IsAccessTypeChanged = true;
                        }
                    }
                }
            }

            // 处理颜色和显示名称
            if (string.IsNullOrEmpty(entry.Color))
            {
                var code = entry.Code.ToLower();
                switch (code)
                {
                    case "read":
                        entry.Color = "orange";
                        entry.Display = "读取";
                        break;
                    case "getpage":
                        entry.Color = "orange";
                        entry.Display = "分页";
                        break;
                    case "create":
                        entry.Color = "green";
                        entry.Display = "创建";
                        break;
                    case "update":
                        entry.Color = "blue";
                        entry.Display = "更新";
                        break;
                    case "delete":
                        entry.Color = "red";
                        entry.Display = "删除";
                        break;
                    case "getbyid":
                        entry.Color = "purple";
                        entry.Display = "按Id读取";
                        break;
                    case "getbyname":
                        entry.Color = "gold";
                        entry.Display = "按名读取";
                        break;
                    default:
                        entry.Color = new List<string> { "orange", "purple", "gold", "magenta", "lime", "volcano", "geekblue", "gray", "cyan" }.NextItem();
                        break;
                }
            }
        }
        return target.ToTextJson();
    }

    /// <summary>
    /// 删除指定Id的资源
    /// </summary>
    /// <param name="id">资源Id</param>
    /// <param name="token"></param>
    /// <returns></returns>
    public async Task<ApiResult> DeleteResourceAsync(long id, CancellationToken token = default)
    {
        var resource = await _repository.FindAsync(id, token);
        if (resource == null)
        {
            return new ApiResult(ApiResultType.Success, "资源已删除");
        }

        if (resource.IsSystem)
        {
            return ApiResult.Error($"系统资源{resource.Name}不允许删除");
        }

        resource.ClearRoleResources();

        // 资源删除集成事件
        var eventDto = new ResourceIntegrationEventDto(resource.Code, resource.Type);
        var serviceName = provider.GetServiceName();
        resource.AddIntegrationEvent(new ResourceDeletedIntegrationEvent([eventDto], serviceName));

        await _repository.DeleteAsync(resource, token);

        _logger.LogInformation($"删除服务 {resource.ServiceName} 的 {resource.Type}资源：{resource.Id}.{resource.Name}[{resource.Code}]");
        return ApiResult.Success($"资源 {resource.Name} 删除成功");
    }

    /// <summary>
    /// 删除指定服务中的指定类型的所有资源
    /// </summary>
    /// <param name="serviceName">服务名称</param>
    /// <param name="type">资源类型</param>
    /// <param name="token"></param>
    /// <returns></returns>
    public async Task<ApiResult> DeleteResourceAsync(string serviceName, ResourceType type, CancellationToken token = default)
    {
        var specification = new ResourceByServiceNameAndTypeSpec(serviceName, type);
        var resourceIds = await _repository.GetAllAsync(specification, m => m.Id, token);
        if (resourceIds.Count == 0)
        {
            return ApiResult.NoChanged();
        }

        foreach (var id in resourceIds)
        {
            await DeleteResourceAsync(id, token);
        }

        return ApiResult.Success($"服务 {serviceName} 的 {type} 资源删除成功");
    }

    /// <summary>
    /// 设置角色资源的操作权限
    /// </summary>
    /// <param name="roleId">角色编号</param>
    /// <param name="dtos">角色资源信息</param>
    /// <param name="token">取消令牌</param>
    /// <returns>业务操作结果</returns>
    public async Task<ApiResult<IRoleResource[]>> SetRoleResourceAsync(long roleId, RoleResourceInDto[] dtos, CancellationToken token)
    {
        var resources = new List<Resource>();
        var roleResources = new List<IRoleResource>();
        foreach (var dto in dtos)
        {
            var spec = new ResourceByIdSpec(dto.ResourceId).And(new ResourceIncludeRoleResourceSpec());
            var resource = await _repository.GetAsync(spec, token);
            if (resource == null)
            {
                return ApiResult<IRoleResource[]>.Error($"编号为 {dto.ResourceId} 的资源信息不存在");
            }

            var roleResource = resource.SetRoleResource(roleId, dto.Operations, new Dictionary<string, Dictionary<DataOperation, FilterGroup>>());
            roleResources.Add(roleResource);
            resources.Add(resource);
        }

        var uow = provider.GetUnitOfWork();
        var serviceName = provider.GetServiceName();
        uow.AddIntegrationEvent(new RoleResourceSetIntegrationEvent([.. dtos.Select(m => m.ResourceId)], serviceName));

        var count = await _repository.UpdateAsync(resources, token);

        return count > 0
            ? ApiResult<IRoleResource[]>.Success("角色资源权限设置成功", [.. roleResources])
            : ApiResult<IRoleResource[]>.NoChanged();
    }

    /// <summary>
    /// 设置角色资源的过滤组
    /// </summary>
    /// <param name="resourceId">资源编号</param>
    /// <param name="roleId">角色编号</param>
    /// <param name="filterGroups">过滤组字典</param>
    /// <param name="token">取消令牌</param>
    /// <returns>业务操作结果</returns>
    public async Task<ApiResult<IRoleResource>> SetFilterGroupsAsync(long resourceId, long roleId, IDictionary<string, Dictionary<DataOperation, FilterGroup>> filterGroups, CancellationToken token)
    {
        var spec = new ResourceByIdSpec(resourceId).And(new ResourceIncludeRoleResourceSpec());
        var resource = await _repository.GetAsync(spec, token);
        if (resource == null)
        {
            return ApiResult<IRoleResource>.Error($"编号为 {resourceId} 的资源信息不存在");
        }

        var roleResource = resource.RoleResources.FirstOrDefault(m => m.RoleId == roleId);
        if (roleResource == null)
        {
            return ApiResult<IRoleResource>.Error($"角色 {roleId} 的资源权限信息不存在");
        }

        // 获取现有的操作权限
        var operations = roleResource.GetOperations();
        roleResource = resource.SetRoleResource(roleId, operations, filterGroups);

        var serviceName = provider.GetServiceName();
        resource.AddIntegrationEvent(new RoleResourceSetIntegrationEvent([resourceId], serviceName));

        await _repository.UpdateAsync(resource, token);

        return ApiResult<IRoleResource>.Success("角色资源过滤组设置成功", roleResource);
    }

    public async Task<ApiResult> RemoveRoleResourceAsync(long roleId, long[] resourceIds, CancellationToken token)
    {
        var spec = new ResourceByIdsSpec(resourceIds).And(new ResourceIncludeRoleResourceSpec());
        var resources = await _repository.GetAllAsync(spec, token);
        if (resources.Count == 0)
        {
            return ApiResult.NoChanged();
        }

        foreach (var resource in resources)
        {
            resource.RemoveRoleResource(roleId);
        }

        var uow = provider.GetUnitOfWork();
        var serviceName = provider.GetServiceName();
        uow.AddIntegrationEvent(new RoleResourceRemovedIntegrationEvent([.. resourceIds], serviceName));
        var count = await _repository.UpdateAsync(resources, token);
        return count > 0 ? ApiResult.Success("角色资源权限删除成功") : ApiResult.NoChanged();
    }
}
